package com.onlyxiahui.array;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.alibaba.fastjson.util.ParameterizedTypeImpl;
import com.alibaba.fastjson.util.TypeUtils;
import com.onlyxiahui.common.action.description.DocumentContext;
import com.onlyxiahui.common.action.description.bean.PropertyData;
import com.onlyxiahui.common.action.description.box.JsonDataTypeBox;
import com.onlyxiahui.common.action.description.handler.PropertyHandler;
import com.onlyxiahui.common.action.description.util.ActionClassUtil;
import com.onlyxiahui.common.action.description.util.ActionIgnoreUtil;
import com.onlyxiahui.common.action.description.util.ActionReflectUtil;
import com.thoughtworks.qdox.model.JavaField;

/**
 * Description <br>
 * Date 2020-01-07 17:50:39<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */

public abstract class BasePropertyHandler2 implements PropertyHandler {

	public boolean ignore(Field field) {
		return ActionIgnoreUtil.isIgnore(field);
	}

	public List<PropertyData> list(Class<?> propertyClass, Type type, DocumentContext context) {

		List<PropertyData> list = new ArrayList<>();

		if (ActionClassUtil.isPrimitive(propertyClass) || ActionClassUtil.isBaseProperty(propertyClass)) {

		} else if (Collection.class.isAssignableFrom(propertyClass) && type != null) {
			collectionParameterizedType("", propertyClass, type, context, list);
		} else if (ActionClassUtil.isBaseEntityMap(propertyClass) && type != null) {
			map(propertyClass, type, context, list);
		} else if (propertyClass.isArray()) {
			array(propertyClass, type, context, list);
		} else {
			bean(propertyClass, type, context, list);
		}
		return list;
	}

	private void bean(Class<?> propertyClass, Type type, DocumentContext context, List<PropertyData> list) {
		String className = null == propertyClass ? "" : propertyClass.getName();
		boolean isParameterizedType = (type instanceof ParameterizedType);

		List<PropertyDescriptor> propertyList = new ArrayList<>();// ActionBeanPropertyBox.getWritePropertyList(propertyClass);
		for (PropertyDescriptor pd : propertyList) {
			// Class<?> clazz = pd.getPropertyType();
			String name = pd.getName();

			Field field = ActionReflectUtil.getField(propertyClass, Object.class, name);

			if (null != field && !ignore(field)) {

				// 得到field的class及类型全路径
				Class<?> fieldClass = field.getType();
				// 关键的地方，如果是List类型，得到其Generic的类型
				Type fieldType = field.getGenericType();

				if (isParameterizedType) {
					if (propertyClass != null && fieldClass == Object.class && fieldType instanceof TypeVariable) {
						TypeVariable<?> tv = (TypeVariable<?>) fieldType;
						Type genericFieldType = getInheritGenericType(propertyClass, type, tv);
						if (genericFieldType != null) {
							fieldClass = TypeUtils.getClass(genericFieldType);
							fieldType = genericFieldType;
						}
					}
				}

				JavaField jf = context.getSourceBox().getJavaFieldByName(className, name);

				PropertyData data = new PropertyData();
				data.setName(name);
				data.setType(JsonDataTypeBox.getType(fieldClass));
				if (null != jf) {
					String description = jf.getComment();
					data.setDescription(description);
				}
				List<PropertyData> nodes = list(fieldClass, fieldType, context);
				data.setNodes(nodes);
				list.add(data);
			}
		}
	}

	private void array(Class<?> propertyClass, Type type, DocumentContext context, List<PropertyData> list) {
		Type fieldType = null;
		if (type instanceof GenericArrayType) {
			fieldType = ((GenericArrayType) type).getGenericComponentType();
		}
		// GenericArrayType
		Class<?> genericClass = propertyClass.getComponentType();
		PropertyData data = new PropertyData();
		data.setType(JsonDataTypeBox.getType(genericClass));

		List<PropertyData> nodes = list(genericClass, fieldType, context);
		data.setNodes(nodes);
		list.add(data);
	}

	private void map(Class<?> propertyClass, Type type, DocumentContext context, List<PropertyData> list) {
		if (type instanceof ParameterizedType) {
			PropertyData data = new PropertyData();
			data.setType(JsonDataTypeBox.getType(propertyClass));
			data.setName("key");
			list.add(data);

			ParameterizedType parameterizedType = (ParameterizedType) type;
			Type[] types = parameterizedType.getActualTypeArguments();
			if (types.length > 1) {
				// Type keyType = types[0];
				Type valueType = types[0];
				if (valueType instanceof Class<?>) {
					Class<?> valueGenericClass = (Class<?>) types[1];
					List<PropertyData> nodes = list(valueGenericClass, null, context);

					List<PropertyData> ns = new ArrayList<>();

					PropertyData fieldData = new PropertyData();
					fieldData.setType(JsonDataTypeBox.getType(valueGenericClass));
					fieldData.setNodes(nodes);

					ns.add(fieldData);

					data.setNodes(ns);
				} else if (valueType instanceof ParameterizedType) {
					ParameterizedType fieldType = (ParameterizedType) valueType;
					Type rawType = fieldType.getRawType();
					if (rawType instanceof Class) {
						Class<?> classType = (Class<?>) rawType;
						List<PropertyData> nodes = list(classType, fieldType, context);
						data.setNodes(nodes);
					}
				}
			}
		}
	}

	private void collectionParameterizedType(String name, Class<?> propertyClass, Type type, DocumentContext context, List<PropertyData> list) {
		if (type instanceof ParameterizedType) {
			ParameterizedType parameterizedType = (ParameterizedType) type;
			// Type fieldType = parameterizedType.getRawType();
			Type[] types = parameterizedType.getActualTypeArguments();
			if (types.length > 0) {
				PropertyData data = new PropertyData();
				data.setName(name);
				data.setType(JsonDataTypeBox.getType(propertyClass));

				Type pt = types[0];
				if (pt instanceof Class<?>) {
					Class<?> genericClass = (Class<?>) pt;
					List<PropertyData> nodes = list(genericClass, null, context);

					List<PropertyData> ns = new ArrayList<>();

					PropertyData fieldData = new PropertyData();
					fieldData.setType(JsonDataTypeBox.getType(genericClass));
					fieldData.setNodes(nodes);

					ns.add(fieldData);

					data.setNodes(ns);
				} else if (pt instanceof ParameterizedType) {
					ParameterizedType fieldType = (ParameterizedType) pt;
					Type rawType = fieldType.getRawType();
					if (rawType instanceof Class) {
						Class<?> classType = (Class<?>) rawType;
						List<PropertyData> nodes = list(classType, fieldType, context);
						data.setNodes(nodes);
					}
				}

				list.add(data);
			}
		}
	}

//	public List<PropertyData> get(Class<?> propertyClass, Type type, DocumentContext context) {
//		String className = propertyClass.getName();
//		List<PropertyData> list = new ArrayList<>();
//
//		List<PropertyDescriptor> propertyList = BeanPropertyBox.getWritePropertyList(propertyClass);
//		for (PropertyDescriptor pd : propertyList) {
//			Class<?> clazz = pd.getPropertyType();
//			String name = pd.getName();
//
//			Field field = ReflectUtil.getField(clazz, Object.class, className);
//
//			if (null != field) {
//				// 得到field的class及类型全路径
//				Class<?> classType = field.getType();
//				// 关键的地方，如果是List类型，得到其Generic的类型
//				Type fieldType = field.getGenericType();
//				JavaField jf = context.getSourceBox().getJavaFieldByName(className, name);
//
//				PropertyData data = new PropertyData();
//				data.setName(name);
//				if (null != jf) {
//					String description = jf.getComment();
//					data.setDescription(description);
//				}
//
//				if (PropertyUtil.isPrimitive(classType) || PropertyUtil.isBaseProperty(classType)) {// 【1】判断是否为基本类型
//
//				} else if (Collection.class.isAssignableFrom(classType) && type != null) {
//
//				} else if (Map.class.isAssignableFrom(classType) && type != null) {//
//
//				} else if (Set.class.isAssignableFrom(classType) && type != null) {//
//
//				} else if (Date.class.isAssignableFrom(classType)) {
//
//				} else if (classType.isArray()) {
//
//				} else if (XmlBean.class.isAssignableFrom(classType)) {
//
//				} else {//
//
//				}
//
//				if (ClassUtil.isBean(clazz)) {
//					List<PropertyData> nodes = get(clazz, context);
//					data.setNodes(nodes);
//				}
//				list.add(data);
//			}
//		}
//		return list;
//	}

//	private List<PropertyData> handlerCollectionParameterizedType(String name, Class<?> propertyClass, Type type, DocumentContext context) {
//		if (type instanceof ParameterizedType) {
//
//			// Type fieldType = parameterizedType.getRawType();
//
//			ParameterizedType parameterizedType = (ParameterizedType) type;
//			Type[] types = parameterizedType.getActualTypeArguments();
//
//			if (types.length > 0) {
//				PropertyData data = new PropertyData();
//				data.setName(name);
//				data.setType(JsonDataTypeBox.getType(propertyClass));
//
//				Type pt = types[0];
//				if (pt instanceof Class<?>) {
//					Class<?> genericClass = (Class<?>) pt;
//					List<PropertyData> nodes = list(genericClass, null, context);
//
//					List<PropertyData> ns = new ArrayList<>();
//
//					PropertyData fieldData = new PropertyData();
//					fieldData.setType(JsonDataTypeBox.getType(genericClass));
//					fieldData.setNodes(nodes);
//
//					ns.add(fieldData);
//
//					data.setNodes(ns);
//				} else if (pt instanceof ParameterizedType) {
//					ParameterizedType fieldType = (ParameterizedType) pt;
//					Type rawType = fieldType.getRawType();
//					if (rawType instanceof Class) {
//						Class<?> classType = (Class<?>) rawType;
//						List<PropertyData> nodes = list(classType, fieldType, context);
//						data.setNodes(nodes);
//					}
//				}
//			}
//		}
//	}
//
//	private PropertyData handleProperty(Class<?> beanClass,Class<?> propertyClass,String name,Field field, DocumentContext context) {
//		PropertyData pd=null;
//		if (null != field && !ignore(field)) {
//
//			// 得到field的class及类型全路径
//			Class<?> fieldClass = field.getType();
//			// 关键的地方，如果是List类型，得到其Generic的类型
//			Type fieldType = field.getGenericType();
//
//			if (isParameterizedType) {
//				if (propertyClass != null && fieldClass == Object.class && fieldType instanceof TypeVariable) {
//					TypeVariable<?> tv = (TypeVariable<?>) fieldType;
//					Type genericFieldType = getInheritGenericType(propertyClass, type, tv);
//					if (genericFieldType != null) {
//						fieldClass = TypeUtils.getClass(genericFieldType);
//						fieldType = genericFieldType;
//					}
//				}
//			}
//
//			JavaField jf = context.getSourceBox().getJavaFieldByName(className, name);
//
//			PropertyData data = new PropertyData();
//			data.setName(name);
//			data.setType(JsonDataTypeBox.getType(fieldClass));
//			if (null != jf) {
//				String description = jf.getComment();
//				data.setDescription(description);
//			}
//			List<PropertyData> nodes = list(fieldClass, fieldType, context);
//			data.setNodes(nodes);
//			list.add(data);
//	}

	private static Type getInheritGenericType(Class<?> clazz, Type type, TypeVariable<?> tv) {
		GenericDeclaration gd = tv.getGenericDeclaration();

		Class<?> class_gd = null;
		if (gd instanceof Class) {
			class_gd = (Class<?>) tv.getGenericDeclaration();
		}

		Type[] arguments = null;
		if (class_gd == clazz) {
			if (type instanceof ParameterizedType) {
				ParameterizedType ptype = (ParameterizedType) type;
				arguments = ptype.getActualTypeArguments();
			}
		} else {
			for (Class<?> c = clazz; c != null && c != Object.class && c != class_gd; c = c.getSuperclass()) {
				Type superType = c.getGenericSuperclass();

				if (superType instanceof ParameterizedType) {
					ParameterizedType p_superType = (ParameterizedType) superType;
					Type[] p_superType_args = p_superType.getActualTypeArguments();
					getArgument(p_superType_args, c.getTypeParameters(), arguments);
					arguments = p_superType_args;
				}
			}
		}

		if (arguments == null || class_gd == null) {
			return null;
		}

		Type actualType = null;
		TypeVariable<?>[] typeVariables = class_gd.getTypeParameters();
		for (int j = 0; j < typeVariables.length; ++j) {
			if (tv.equals(typeVariables[j])) {
				actualType = arguments[j];
				break;
			}
		}

		return actualType;
	}

	@SuppressWarnings("rawtypes")
	private static boolean getArgument(Type[] typeArgs, TypeVariable[] typeVariables, Type[] arguments) {
		if (arguments == null || typeVariables.length == 0) {
			return false;
		}

		boolean changed = false;
		for (int i = 0; i < typeArgs.length; ++i) {
			Type typeArg = typeArgs[i];
			if (typeArg instanceof ParameterizedType) {
				ParameterizedType p_typeArg = (ParameterizedType) typeArg;
				Type[] p_typeArg_args = p_typeArg.getActualTypeArguments();
				boolean p_changed = getArgument(p_typeArg_args, typeVariables, arguments);
				if (p_changed) {
					typeArgs[i] = new ParameterizedTypeImpl(p_typeArg_args, p_typeArg.getOwnerType(), p_typeArg.getRawType());
					changed = true;
				}
			} else if (typeArg instanceof TypeVariable) {
				for (int j = 0; j < typeVariables.length; ++j) {
					if (typeArg.equals(typeVariables[j])) {
						typeArgs[i] = arguments[j];
						changed = true;
					}
				}
			}
		}

		return changed;
	}
}
